import {
  asTerminalIcon,
  ICreateClientWithWidgetOptions,
  ICreateTerminalOptions,
  IShellLaunchConfig,
  IStartExtensionTerminalRequest,
  ITerminalBrowserHistory,
  ITerminalClient,
  ITerminalClientFactory2,
  ITerminalController,
  ITerminalExitEvent,
  ITerminalInfo,
  ITerminalInternalService,
  ITerminalLaunchError,
  ITerminalProcessExtHostProxy,
  ITerminalProfile,
  ITerminalTitleChangeEvent,
  IWidget,
  TERMINAL_ID_SEPARATOR,
  TerminalCliterFilter,
  TerminalLocation,
  TerminalOptions,
} from '../common';
import { IEventBus, IThemeColor, WithEventBus } from '../../common';
import {
  Deferred,
  Emitter,
  Event,
  Uri,
  withNullAsUndefined,
} from '../../utils';
import isThemeColor = IThemeColor.isThemeColor;
import { TerminalGroupViewService } from './terminal.view';
import { IApplicationService } from '../../common/application';

export class TerminalController
  extends WithEventBus
  implements ITerminalController
{
  // 终端是否聚焦
  protected _focus: boolean;
  // 终端客户端集合，key 为终端 id
  protected _clients: Map<string, ITerminalClient>;
  // 终端打开事件
  protected _onDidOpenTerminal = new Emitter<ITerminalInfo>();
  // 终端关闭事件
  protected _onDidCloseTerminal = new Emitter<ITerminalExitEvent>();
  // 终端标题变更事件
  protected _onDidTerminalTitleChange =
    new Emitter<ITerminalTitleChangeEvent>();
  // 活动终端变更事件
  protected _onDidChangeActiveTerminal = new Emitter<string>();
  // 终端控制器就绪的异步信号
  protected _ready = new Deferred<void>();
  // 当前激活的终端客户端 id
  protected _activeClientId?: string;

  // 终端相关事件只读属性
  readonly onDidOpenTerminal: Event<ITerminalInfo> =
    this._onDidOpenTerminal.event;
  readonly onDidCloseTerminal: Event<ITerminalExitEvent> =
    this._onDidCloseTerminal.event;
  readonly onDidTerminalTitleChange: Event<ITerminalTitleChangeEvent> =
    this._onDidTerminalTitleChange.event;
  readonly onDidChangeActiveTerminal: Event<string> =
    this._onDidChangeActiveTerminal.event;

  // 扩展终端启动请求事件
  private readonly _onInstanceRequestStartExtensionTerminal =
    new Emitter<IStartExtensionTerminalRequest>();
  readonly onInstanceRequestStartExtensionTerminal: Event<IStartExtensionTerminalRequest> =
    this._onInstanceRequestStartExtensionTerminal.event;

  // 当前终端客户端 id
  private _clientId: string;

  // 视图就绪的异步信号
  public viewReady = new Deferred<void>();

  // 服务依赖
  private terminalView: TerminalGroupViewService;
  private clientFactory: ITerminalClientFactory2;
  private service: ITerminalInternalService;
  private applicationService: IApplicationService;

  // 获取所有终端客户端
  get clients() {
    return this._clients;
  }

  // 获取终端是否聚焦
  get focused() {
    return this._focus;
  }

  // 获取终端控制器就绪信号
  get ready() {
    return this._ready;
  }

  // 获取当前激活的终端客户端
  get activeClient() {
    if (this._activeClientId) {
      return this._clients.get(this._activeClientId);
    }
  }

  // 获取当前终端客户端 id
  get clientId() {
    if (this._clientId) {
      return this._clientId;
    }
    this._clientId = this.applicationService.clientId;
    return this._clientId;
  }

  /**
   * 创建终端客户端，如果已存在则直接返回已存在的客户端
   */
  private async _createClientOrIgnore(widget: IWidget) {
    // 如果已存在该 widget id 的客户端，直接返回
    if (this._clients.has(widget.id)) {
      return this._clients.get(widget.id)!;
    }
    // 否则创建新的客户端
    return await this._createClient(widget);
  }

  /**
   * 创建终端客户端
   */
  private async _createClient(
    widget: IWidget,
    options?: ICreateTerminalOptions
  ) {
    // 通过工厂方法创建终端客户端
    const client = await this.clientFactory(widget, options);
    // 设置客户端相关事件和状态
    return this.setupClient(widget, client);
  }

  /**
   * 设置终端客户端相关事件和状态
   */
  setupClient(widget: IWidget, client: ITerminalClient) {
    // 将客户端加入集合
    this._clients.set(client.id, client);
    // 监听终端退出事件
    client.addDispose(
      client.onExit((e) => {
        // 在这个函数内不要 removeWidget，会导致 TerminalTask 场景下任务执行完毕直接退出而不是用户手动触发 onKeyDown 退出

        this._onDidCloseTerminal.fire({ id: client.id, code: e.code });
      })
    );

    // 监听终端标题变更事件
    client.addDispose(
      client.onTitleChange((e) => {
        this._onDidTerminalTitleChange.fire({ id: client.id, name: e.name });
      })
    );

    // 客户端销毁时从集合中移除，并触发关闭事件
    client.addDispose({
      dispose: () => {
        this._clients.delete(client.id);
        this._onDidCloseTerminal.fire({ id: client.id, code: -1 });
      },
    });

    // 触发终端打开事件
    this._onDidOpenTerminal.fire({
      id: client.id,
      name: client.name,
      isActive: false,
    });

    // 选中当前 widget
    this.terminalView.selectWidget(widget.id);

    return client;
  }

  /**
   * 销毁终端客户端
   */
  private _disposeClient(widget: IWidget) {
    const client = this.findClientFromWidgetId(widget.id);
    client && client.dispose();
  }

  /**
   * 构造函数，初始化终端控制器
   */
  constructor(
    eventBus: IEventBus,
    terminalView: TerminalGroupViewService,
    clientFactory: ITerminalClientFactory2,
    service: ITerminalInternalService,
    applicationService: IApplicationService
  ) {
    super(eventBus);
    this.terminalView = terminalView;
    this.clientFactory = clientFactory;
    this.service = service;
    this.applicationService = applicationService;
    this._focus = false;
    this._clients = new Map();
  }

  /**
   * 创建一个终端分组
   */
  private _createOneGroup(options?: IShellLaunchConfig) {
    const index = this.terminalView.createGroup(options);
    const group = this.terminalView.getGroup(index);
    return { group, index };
  }

  /**
   * 重置终端，创建一个新的分组和 widget
   */
  private async _reset() {
    const { group } = this._createOneGroup();
    const widget = await this.terminalView.createWidget(group);
    return widget;
  }

  async recovery(history: ITerminalBrowserHistory) {
    return;
  }

  /**
   * 首次初始化终端控制器
   */
  async firstInitialize() {
    // 等待主布局和终端视图就绪，最多等待 2 秒
    await Promise.race([
      (async () => {
        await this.viewReady.promise;
      })(),
      new Promise<void>((resolve) => {
        setTimeout(() => {
          resolve();
        }, 2000);
      }),
    ]);

    // 监听终端 widget 创建事件，自动创建终端客户端
    this.addDispose(
      this.terminalView.onWidgetCreated((widget) => {
        this._createClientOrIgnore(widget);
      })
    );

    // 监听终端 widget 销毁事件，自动销毁终端客户端
    this.addDispose(
      this.terminalView.onWidgetDisposed((widget) => {
        this._disposeClient(widget);
      })
    );

    // 监听终端 widget 全部关闭事件，隐藏终端面板
    this.addDispose(
      this.terminalView.onWidgetEmpty(() => {
        this.hideTerminalPanel();
      })
    );

    // 监听终端 widget 选中事件，切换激活终端并聚焦
    this.addDispose(
      this.terminalView.onWidgetSelected((widget) => {
        const client = this.findClientFromWidgetId(widget.id);
        if (client) {
          this._onDidChangeActiveTerminal.fire(client.id);
          if (client.ready) {
            // 事件是同步触发的，界面可能还没更新完成，加延迟确保聚焦
            setTimeout(() => {
              client.focus();
            });
          }
        }
        this._activeClientId = client?.id;
      })
    );

    // 标记终端控制器已就绪
    this._ready.resolve();

    const current = await this._reset();
    this.terminalView.selectWidget(current.id);
  }

  /**
   * 重新连接所有终端客户端，检查健康状态
   */
  async reconnect() {
    const clients = Array.from(this._clients.values());

    // 在重连时检查每个终端的健康状态，不健康则提示
    clients.forEach(async (client) => {
      const clientHealthy = await client.checkHealthy();

      if (!clientHealthy) {
        client.displayUnHealthyMessage();
      }
    });
  }

  /**
   * 聚焦终端
   */
  focus() {
    this._focus = true;
  }

  /**
   * 失焦终端
   */
  blur() {
    this._focus = false;
  }

  /**
   * 处理终端实例的右键菜单事件
   */
  onContextMenu(e: React.MouseEvent<HTMLElement>): void {}

  /**
   * 销毁指定条件的终端客户端
   */
  disposeTerminalClients(filter?: TerminalCliterFilter) {
    const clients = Array.from(this.clients.values()).filter(
      (client) =>
        client.id === filter?.id ||
        client.isTaskExecutor === filter?.isTaskExecutor ||
        client.name === filter?.name
    );

    for (const client of clients) {
      try {
        client.dispose();
      } catch (err) {
        //
      }
    }
  }

  /**
   * 根据 widgetId 查找终端客户端
   */
  findClientFromWidgetId(widgetId: string) {
    return this._clients.get(widgetId);
  }

  /**
   * 将 TerminalOptions 转换为终端启动配置
   */
  public convertTerminalOptionsToLaunchConfig(
    options: TerminalOptions
  ): IShellLaunchConfig {
    const shellLaunchConfig: IShellLaunchConfig = {
      name: options.name,
      executable: withNullAsUndefined(options.shellPath),
      args: withNullAsUndefined(options.shellArgs),
      cwd: withNullAsUndefined(options.cwd),
      env: withNullAsUndefined(options.env),
      icon: withNullAsUndefined(asTerminalIcon(options.iconPath)),
      color: isThemeColor(options.color) ? options.color.id : undefined,
      initialText: withNullAsUndefined(options.message),
      strictEnv: withNullAsUndefined(options.strictEnv),
      hideFromUser: withNullAsUndefined(options.hideFromUser),
      location: TerminalLocation.Panel,
      disablePersistence: withNullAsUndefined(options.isTransient),
    };
    shellLaunchConfig.__fromTerminalOptions = options;
    return shellLaunchConfig;
  }

  /**
   * 将配置或 profile 转换为终端启动配置
   */
  convertProfileToLaunchConfig(
    shellLaunchConfigOrProfile:
      | IShellLaunchConfig
      | ITerminalProfile
      | undefined,
    cwd?: Uri | string
  ): IShellLaunchConfig {
    if (!shellLaunchConfigOrProfile) {
      return {};
    }
    // 如果是 profile
    if (
      shellLaunchConfigOrProfile &&
      'profileName' in shellLaunchConfigOrProfile
    ) {
      const profile = shellLaunchConfigOrProfile;
      if (!profile.path) {
        return shellLaunchConfigOrProfile;
      }
      return {
        executable: profile.path,
        args: profile.args,
        env: profile.env,
        color: profile.color,
        name: profile.overrideName ? profile.profileName : undefined,
        cwd,
      };
    }

    if (cwd) {
      shellLaunchConfigOrProfile.cwd = cwd;
    }
    return shellLaunchConfigOrProfile;
  }

  /**
   * 通过 TerminalOptions 创建终端及视图
   */
  async createTerminalWithWidgetByTerminalOptions(
    options: ICreateClientWithWidgetOptions
  ) {
    const launchConfig = this.convertTerminalOptionsToLaunchConfig(
      options.terminalOptions
    );
    return this.createTerminalWithWidget({
      ...options,
      config: launchConfig,
    });
  }

  /**
   * 创建终端
   */
  async createTerminal(options: ICreateTerminalOptions) {
    const generateSessionId = await this.service.generateSessionId();

    const widgetId = options.id || generateSessionId;
    const { group } = this._createOneGroup();
    const widget = await this.terminalView.createWidget(
      group,
      widgetId,
      false,
      true
    );

    const client = await this._createClient(widget, options);

    return client;
  }

  /**
   * 通过配置创建终端及视图
   */
  async createTerminalWithWidget(options: ICreateTerminalOptions) {
    const generateSessionId = await this.service.generateSessionId();

    const widgetId = options.id
      ? this.clientId + TERMINAL_ID_SEPARATOR + options.id
      : generateSessionId;

    // 这里转一遍是为了拿到 LaunchConfig，但是在 TerminalClient 内部还是会对 options.config 做可用检测并转换
    const launchConfig = this.convertProfileToLaunchConfig(options.config);

    const { group } = this._createOneGroup(launchConfig);
    const widget = await this.terminalView.createWidget(
      group,
      widgetId,
      options.isTaskExecutor || !options.closeWhenExited,
      true
    );

    if (options.beforeCreate && typeof options.beforeCreate === 'function') {
      options.beforeCreate(widgetId);
    }

    const client = await this._createClient(widget, {
      id: widgetId,
      config: launchConfig,
      cwd: options?.cwd,
      location: options?.location,
      resource: options?.resource,
    });

    if (options.isTaskExecutor) {
      client.isTaskExecutor = true;
      client.taskId = options.taskId;
    }
    return client;
  }

  /**
   * 清空当前分组下所有终端内容
   */
  clearCurrentGroup() {
    const group = this.terminalView.currentGroup.get();
    if (group) {
      const widgets = group.widgets.get();
      widgets.forEach((widget: any) => {
        const client = this._clients.get(widget.id);
        if (client) {
          client.clear();
        }
      });
    }
  }

  /**
   * 清空所有分组下的终端内容
   */
  clearAllGroups() {
    this._clients.forEach((client) => {
      if (client) {
        client.clear();
      }
    });
  }

  /**
   * 显示终端面板
   */
  showTerminalPanel() {
    // 显示终端面板的逻辑
  }

  /**
   * 隐藏终端面板
   */
  hideTerminalPanel() {
    // 隐藏终端面板的逻辑
  }

  /**
   * 切换终端面板显示/隐藏
   */
  toggleTerminalPanel() {
    // 切换终端面板的逻辑
  }

  /**
   * 请求启动扩展终端
   */
  requestStartExtensionTerminal(
    proxy: ITerminalProcessExtHostProxy,
    cols: number,
    rows: number
  ): Promise<ITerminalLaunchError | undefined> {
    // The initial request came from the extension host, no need to wait for it
    return new Promise<ITerminalLaunchError | undefined>((callback) => {
      this._onInstanceRequestStartExtensionTerminal.fire({
        proxy,
        cols,
        rows,
        callback,
      });
    });
  }
}
